package com.yugao.fintech.draper.log.aspect;

import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.HttpUtil;
import com.yugao.fintech.draper.auth.dto.LoginLogDTO;
import com.yugao.fintech.draper.core.model.ResultVO;
import com.yugao.fintech.draper.log.annotation.AuditLog;
import com.yugao.fintech.draper.log.service.AsyncLogService;
import com.yugao.fintech.draper.core.util.WebUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.yugao.fintech.draper.log.enums.BusinessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.context.SecurityContextHolder;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 操作日志记录处理切面
 */
@Slf4j
@Aspect
@RequiredArgsConstructor
public class WebLogAspect {

	private final AsyncLogService asyncLogService;

	/**
	 * 处理OperLog注解，使用环绕通知
	 */
	@SuppressWarnings("unchecked")
	@Around("@annotation(auditLog)")
	public ResultVO<Object> around(ProceedingJoinPoint point, AuditLog auditLog) {
		LoginLogDTO operLog = new LoginLogDTO();
		buildLog(point, auditLog, operLog);

		Long startTime = System.currentTimeMillis();
		ResultVO<Object> resultVO = null;
		try {
			resultVO = (ResultVO<Object>) point.proceed();
		}
		catch (Throwable e) {
			handleException(operLog, e.getLocalizedMessage());
		}
		finally {
			Long endTime = System.currentTimeMillis();
			// 方法执行时间
			long time = endTime - startTime;
			// 赋值
			operLog.setTime(time);
			log.info("异步保存:{}", Thread.currentThread().getId());
			// 异步保存
			asyncLogService.saveLog(operLog);
		}
		return resultVO;
	}

	private void buildLog(JoinPoint point, AuditLog auditLog, LoginLogDTO operLog) {
		HttpServletRequest request = WebUtil.getRequest();
		// 模块名
		String title = auditLog.value();
		// 获取ip地址
		String ip = JakartaServletUtil.getClientIP(request);
		// 请求路径
		String path = URLUtil.getPath(request.getRequestURI());
		// 操作用户
		AtomicReference<String> userName = new AtomicReference<>();
		Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).ifPresent(authentication -> {
			userName.set(authentication.getName());
		});
		// 方法名称
		String method = point.getTarget().getClass().getName() + StringPool.DOT + point.getSignature().getName()
				+ StringPool.LEFT_BRACKET + StringPool.RIGHT_BRACKET;
		// 请求方式
		String requestMethod = request.getMethod();
		// 请求参数
		String param = null;
		if (auditLog.isSaveRequestData()) {
			param = HttpUtil.toParams(request.getParameterMap());
		}
		// 业务类型
		Integer businessType = auditLog.businessType().getValue();
		log.info("方法:{}", method);

		// 赋值
		operLog.setTitle(title)
			.setOperIp(ip)
			.setOperUrl(path)
			.setOperName(userName.get())
			.setMethod(method)
			.setRequestMethod(requestMethod)
			.setOperParam(param)
			.setBusinessType(businessType);
	}

	private void handleException(LoginLogDTO operLog, String msg) {
		operLog.setStatus(BusinessStatus.FAIL.getValue());
		operLog.setErrorMsg(StringUtils.substring(msg, 0, 2000));
	}
}
